pub(crate) mod db;

use std::time::Duration;
use std::collections::HashMap;
use tera::Tera;
use lazy_static::lazy_static;
use moka::sync::CacheBuilder;
use moka::sync::SegmentedCache;
use serde_json::{from_value, Value};
use actix_web::{http::header, HttpResponse, Responder};
use crate::rules::Vo;

lazy_static! {
    pub static ref G_TPL: Tera = {
        let template_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/webapp/html/**/*");
        let mut tera = Tera::new(template_dir).unwrap();
        tera.autoescape_on(vec![]);
        tera.register_function("has_btn", has_btn());
        tera
    };
    pub static ref G_CACHE: SegmentedCache<String, Vec<Vo>> = {
        CacheBuilder::new(1000000).segments(16)
            .time_to_live(Duration::from_secs(43200))//12小时
            .build()
    };
}

// 跳转到给定链接
pub fn redirect(url: &str) -> impl Responder {
    HttpResponse::Found().header(header::LOCATION, url).finish()
}

// 序列化单个对象
#[inline(always)]
pub fn to_json_obj(res: HashMap<String, &str>) -> impl Responder {
    let result = serde_json::to_string(&res).unwrap();
    
    HttpResponse::Ok()
        .content_type("application/json")
        .body(result)
}

// 序列化对象列表
#[inline(always)]
pub fn to_json_list(res: HashMap<String, Value>) -> impl Responder {
    let result = serde_json::to_string(&res).unwrap();
    
    HttpResponse::Ok()
        .content_type("application/json")
        .body(result)
}

// 向模版中传递数据
#[inline(always)]
pub fn ctx(ctx: tera::Context, session: &actix_session::Session) -> tera::Context {
    let user_id = session.get::<u32>("userId").unwrap().unwrap();
    let flag = session.get::<u8>("flag").unwrap().unwrap();
    let key = format!("menus{}", user_id);
    let mut context = ctx;
    context.insert("flag", &flag);
    context.insert("userId", &user_id);
    context.insert("menus", &G_CACHE.get(&key));
    context.insert("ctx", &crate::CONTEXT_PATH);
    
    context
}

// 判断用户是否有按钮权限
fn has_btn() -> impl tera::Function {
    Box::new(
        move |args: &HashMap<String, Value>| -> tera::Result<Value> {
            match args.get("btn") {
                Some(val) => match from_value::<String>(val.clone()) {
                    Ok(btn) => {
                        let user_id = args.get("userId").unwrap().as_u64().unwrap();
                        let key = format!("menus{}", user_id);
                        let menus = G_CACHE.get(&key).unwrap();
                        for vo in menus.iter() {
                            if vo.str("types") == "3" && btn == vo.str("url") {
                                return Ok(true.into());
                            }
                        }

                        Ok(false.into())
                    }
                    Err(_) => Ok(false.into()),
                },
                None => Ok(false.into()),
            }
        },
    )
}

// 根据查询参数的个数计算String的初始容量
#[inline(always)]
pub fn cap(len: usize) -> usize {
    if len == 0_usize {
        100_usize
    } else {
        len << 5
    }
}
